home *** CD-ROM | disk | FTP | other *** search
/ Beginning Mac Programming / Beginning Mac Programming.bin / pc / Open Me for REALbasic 3 / REALbasic 3.2 / Goodies / REAL Software / REALbasic Plug-ins SDK / Examples / obsolete / SelectFolder / SelectFolder.cpp next >
Encoding:
C/C++ Source or Header  |  2000-03-23  |  14.0 KB  |  554 lines

  1. #include "rb_plugin.h"
  2. #include <Navigation.h>
  3.  
  4. #ifdef WIN32
  5.  
  6. static Boolean gSelectFolderOK;
  7. static char gSelectFolderPath[_MAX_PATH];
  8.  
  9. LRESULT CALLBACK OKSubclass(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
  10. UINT APIENTRY SelectFolderHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
  11.  
  12. LRESULT CALLBACK OKSubclass(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
  13. {
  14.     WNDPROC oldWndProc = (WNDPROC) GetProp(hWnd, "SubOldWnd");
  15.     switch (message)
  16.     {
  17.         case WM_COMMAND:
  18.             if (LOWORD(wParam) == IDOK)
  19.             {
  20.                 gSelectFolderOK = true;
  21.                 PostMessage(hWnd, WM_COMMAND, IDCANCEL, 0);
  22.                 return TRUE;
  23.             }
  24.             break;
  25.         case WM_DESTROY:
  26.             SetWindowLong(hWnd, GWL_WNDPROC, (long) oldWndProc);
  27.             RemoveProp(hWnd, "SubOldWnd");
  28.             RemoveProp(hWnd, "SubHookWnd");
  29.             break;
  30.     }
  31.     return CallWindowProc(oldWndProc, hWnd, message, wParam, lParam);
  32. }
  33.  
  34. UINT APIENTRY SelectFolderHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
  35. {
  36.     LPNMHDR lpnm;
  37.     HWND hParent, hOK;
  38.     int len;
  39.     char szFolderPath[_MAX_PATH];
  40.  
  41.     switch (message)
  42.     {
  43.         case WM_INITDIALOG:
  44.             hParent = GetParent(hDlg);
  45.             SetProp(hParent, "SubHookWnd", hDlg);
  46.             SetProp(hParent, "SubOldWnd", (void *) GetWindowLong(hParent, GWL_WNDPROC));
  47.             SetWindowLong(hParent, GWL_WNDPROC, (long) OKSubclass);
  48.             hOK = GetDlgItem(hParent, IDOK);
  49.             break;
  50.         case WM_NOTIFY:
  51.             lpnm = (LPNMHDR) lParam;
  52.             switch (lpnm->code)
  53.             {
  54.                 case CDN_FOLDERCHANGE:
  55.                     len = CommDlg_OpenSave_GetFolderPath(GetParent(hDlg), szFolderPath, _MAX_PATH);
  56.                     strcpy(gSelectFolderPath, szFolderPath);
  57.                     break;
  58.             }
  59.             break;
  60.     }
  61.     return FALSE;
  62. }
  63.  
  64. static REALfolderItem SelectFolder(void)
  65. {
  66.     OPENFILENAME ofn;
  67.     char szPath[_MAX_PATH];
  68.  
  69.     szPath[0] = 0;
  70.  
  71.     memset(&ofn, 0, sizeof(ofn));
  72.     ofn.lStructSize = sizeof(ofn);
  73.     ofn.Flags = OFN_EXPLORER | OFN_ENABLEHOOK | OFN_HIDEREADONLY;
  74.     ofn.lpstrFile = szPath;
  75.     ofn.nMaxFile = _MAX_PATH;
  76.     ofn.lpstrFilter = "Directories\0*.~~~~\0\0";
  77.     ofn.lpfnHook = SelectFolderHook;
  78.     GetOpenFileName(&ofn);
  79.  
  80.     if (gSelectFolderOK)
  81.         return REALFolderItemFromPath(gSelectFolderPath);
  82.     else
  83.         return nil;
  84. }
  85. #else
  86. #include <ToolUtils.h>
  87. #include <Folders.h>
  88. #include <StandardFile.h>
  89. #include <Aliases.h>
  90. #include <A4Stuff.h>
  91.  
  92. enum {
  93.     kSelectItem = 10,             // select button item number
  94.     kSFGetFolderDlgID = 3000,    // dialog resource number
  95.     kStrListID = 3000,            // our strings
  96.     kSelectStrNum = 1,            // word 'Select: ' for button
  97.     kDesktopStrNum = 2,            // word 'Desktop' for button
  98.     kSelectNoQuoteStrNum = 3,    // word 'Select: ' for button
  99.     
  100.     kUseQuotes = true,            // parameter for SetButtonName
  101.     kDontUseQuotes = false
  102. };
  103.  
  104. // the data we need to pass to our standard file hook routine
  105. // includes a pointer to the dialog, a pointer to the standard
  106. // file reply record (so we can inspect the current selection)
  107. // and a copy of the "previous" file spec of the reply record
  108. // so we can see if the selection has changed
  109.  
  110. struct UserDataRec {
  111.     StandardFileReply    *sfrPtr;
  112.     FSSpec                oldSelectionFSSpec;
  113.     DialogPtr            theDlgPtr;
  114.     long                 lUseA4;
  115. };
  116. typedef struct UserDataRec
  117.     UserDataRec, *UserDataRecPtr;
  118.  
  119. static void GetLabelString(StringPtr theStr, short stringNum)
  120. {
  121.     GetIndString(theStr, kStrListID, stringNum);
  122. }
  123.  
  124. static void CopyPStr(StringPtr src, StringPtr dest)
  125. {
  126.     BlockMoveData(src, dest, 1 + src[0]);
  127. }
  128.  
  129. static char GetSelectKey(void)
  130. {
  131.     // this is the key used to trigger the select button
  132.     
  133.     // NOT INTERNATIONAL SAVVY; should at least grab it from resources
  134.     
  135.     return 's';
  136. }
  137.  
  138. // SetButtonName sets the name of the Select button in the dialog
  139. //
  140. // To do this, we need to call the Script Manager to truncate the
  141. // label in the middle to fit the button and to merge the button
  142. // name with the word Select (possibly followed by quotes).  Using
  143. // the Script Manager avoids all sorts of problems internationally.
  144. //
  145. // buttonName is the name to appear following the word Select
  146. // quoteFlag should be true if the name is to appear in quotes
  147.  
  148. static void SetButtonName(DialogPtr theDlgPtr, short buttonID, StringPtr buttonName,
  149.                     Boolean quoteFlag)
  150. {
  151.     short     buttonType;
  152.     Handle    buttonHandle;
  153.     Rect    buttonRect;
  154.     short    textWidth;
  155.     Handle    labelHandle;
  156.     Handle    nameHandle;
  157.     Str15    keyStr;
  158.     Str255    labelStr;
  159.     OSErr    err;
  160.     
  161.     nameHandle = nil;
  162.     labelHandle = nil;
  163.     
  164.     // get the details of the button from the dialog
  165.     
  166.     GetDialogItem(theDlgPtr, buttonID, &buttonType, &buttonHandle, &buttonRect);
  167.     
  168.     // get the string for the select button label, "Select ^0" or "Select “^0”"
  169.     
  170.     GetLabelString(labelStr, (quoteFlag == kUseQuotes) ? kSelectStrNum : kSelectNoQuoteStrNum);
  171.     
  172.     // make string handles containing the select button label and the
  173.     // file name to be stuffed into the button
  174.     
  175.     err = PtrToHand(&labelStr[1], &labelHandle, labelStr[0]);
  176.     if (err != noErr) goto Bail;
  177.     
  178.     // cut out the middle of the file name to fit the button
  179.     //
  180.     // we'll temporarily use labelStr here to hold the modified button name
  181.     // since we don't own the buttonName string storage space
  182.     
  183.     textWidth = (buttonRect.right - buttonRect.left) - StringWidth(labelStr);
  184.  
  185.     CopyPStr(buttonName, labelStr);
  186.     (void) TruncString(textWidth, labelStr, smTruncMiddle);
  187.     
  188.     err = PtrToHand(&labelStr[1], &nameHandle, labelStr[0]);
  189.     if (err != noErr) goto Bail;
  190.     
  191.     // replace the ^0 in the Select string with the file name
  192.     
  193.     CopyPStr("\p^0", keyStr);
  194.     
  195.     (void) ReplaceText(labelHandle, nameHandle, keyStr);
  196.     
  197.     labelStr[0] = (unsigned char) GetHandleSize(labelHandle);
  198.     BlockMoveData(*labelHandle, &labelStr[1], labelStr[0]);
  199.     
  200.     // now set the control title, and re-validate the area
  201.     // above the control to avoid a needless redraw
  202.     
  203.     SetControlTitle((ControlHandle) buttonHandle, labelStr);
  204.     
  205.     ValidRect(&buttonRect);
  206.  
  207. Bail:
  208.     if (nameHandle)        DisposeHandle(nameHandle);
  209.     if (labelHandle)    DisposeHandle(labelHandle);
  210.     
  211. }
  212.  
  213. // FlashButton briefly highlights the dialog button 
  214. // as feedback for key equivalents
  215.  
  216. static void FlashButton(DialogPtr theDlgPtr, short buttonID)
  217. {
  218.     short    buttonType;
  219.     Handle    buttonHandle;
  220.     Rect    buttonRect;
  221.     unsigned long    finalTicks;
  222.     
  223.     GetDialogItem(theDlgPtr, buttonID, &buttonType, &buttonHandle, &buttonRect);
  224.     HiliteControl((ControlHandle) buttonHandle, kControlButtonPart);
  225.     Delay(10, &finalTicks);
  226.     HiliteControl((ControlHandle) buttonHandle, 0);
  227. }
  228.  
  229. static Boolean SameFSSpec(FSSpecPtr spec1, FSSpecPtr spec2)
  230. {
  231.     return (spec1->vRefNum == spec2->vRefNum
  232.             && spec1->parID == spec2->parID
  233.             && EqualString(spec1->name, spec2->name, false, false));
  234. }
  235.  
  236. // MyModalDialogFilter maps a key to the Select button, and handles
  237. // flashing of the button when the key is hit
  238.  
  239. static pascal Boolean SFGetFolderModalDialogFilter(DialogPtr theDlgPtr, EventRecord *eventRec,
  240.                                             short *item, Ptr dataPtr)
  241. {
  242. #pragma unused (dataPtr)
  243.  
  244.     // make certain the proper dialog is showing, 'cause standard file
  245.     // can nest dialogs but calls the same filter for each
  246.     
  247.     if (((WindowPeek) theDlgPtr)->refCon == sfMainDialogRefCon)
  248.     {
  249.         // check if the select button was hit
  250.         
  251.         if ((eventRec->what == keyDown)
  252.             && (eventRec->modifiers & cmdKey) 
  253.             && ((eventRec->message & charCodeMask) == GetSelectKey()))
  254.         {
  255.             *item = kSelectItem;
  256.             FlashButton(theDlgPtr, kSelectItem);
  257.             return true;
  258.         }
  259.     }
  260.     return false;
  261. }
  262.  
  263.  
  264. // MyDlgHook is a hook routine that maps the select button to Open
  265. // and sets the Select button name
  266.  
  267. static pascal short SFGetFolderDialogHook(short item, DialogPtr theDlgPtr, Ptr dataPtr)
  268. {
  269.     UserDataRecPtr    theUserDataRecPtr;
  270.     long            desktopDirID;
  271.     short            desktopVRefNum;
  272.     FSSpec            tempSpec;
  273.     Str63            desktopName;
  274.     OSErr            err;
  275.     
  276.     // be sure Std File is really showing us the intended dialog,
  277.     // not a nested modal dialog
  278.     
  279.     if (((WindowPeek) theDlgPtr)->refCon != sfMainDialogRefCon)
  280.     {
  281.         return item;
  282.     }
  283.     
  284.     theUserDataRecPtr = (UserDataRecPtr) dataPtr;
  285.  
  286.     long lSaveA4 = GetCurrentA4();
  287.     SetA4(theUserDataRecPtr->lUseA4);
  288.     
  289.     // map the Select button to Open
  290.     
  291.     if (item == kSelectItem)
  292.     {
  293.         item = sfItemOpenButton;
  294.     }
  295.     
  296.     // find the desktop folder
  297.     
  298.     err = FindFolder(theUserDataRecPtr->sfrPtr->sfFile.vRefNum,
  299.                     kDesktopFolderType, kDontCreateFolder,
  300.                     &desktopVRefNum, &desktopDirID);
  301.     
  302.     if (err != noErr)
  303.     {
  304.         // for errors, get value that won't match any real vRefNum/dirID
  305.         desktopVRefNum = 0;
  306.         desktopDirID = 0;
  307.     }
  308.     
  309.     // change the Select button label if the selection has changed or
  310.     // if this is the first call to the hook
  311.     
  312.     if (item == sfHookFirstCall
  313.         || item == sfHookChangeSelection
  314.         || item == sfHookRebuildList
  315.         || ! SameFSSpec(&theUserDataRecPtr->sfrPtr->sfFile,
  316.                     &theUserDataRecPtr->oldSelectionFSSpec))
  317.     {
  318.         // be sure there is a file name selected
  319.         
  320.         if (theUserDataRecPtr->sfrPtr->sfFile.name[0] != '\0')
  321.         {
  322.             SetButtonName(theDlgPtr, kSelectItem, 
  323.                             theUserDataRecPtr->sfrPtr->sfFile.name, 
  324.                             kUseQuotes);    // true -> use quotes
  325.         }
  326.         else
  327.         {
  328.             // is the desktop selected?
  329.             
  330.             if (theUserDataRecPtr->sfrPtr->sfFile.vRefNum == desktopVRefNum
  331.                 && theUserDataRecPtr->sfrPtr->sfFile.parID == desktopDirID)
  332.             {
  333.                 // set button to "Select Desktop"
  334.                 
  335.                 GetLabelString(desktopName, kDesktopStrNum);
  336.                 SetButtonName(theDlgPtr, kSelectItem, 
  337.                                 desktopName, kDontUseQuotes);    // false -> no quotes
  338.             }
  339.             else
  340.             {
  341.                 // get parent directory's name for the Select button
  342.                 //
  343.                 // passing an empty name string to FSMakeFSSpec gets the
  344.                 // name of the folder specified by the parID parameter
  345.                 
  346.                 (void) FSMakeFSSpec(theUserDataRecPtr->sfrPtr->sfFile.vRefNum,
  347.                     theUserDataRecPtr->sfrPtr->sfFile.parID, "\p",
  348.                     &tempSpec);
  349.                 SetButtonName(theDlgPtr, kSelectItem, 
  350.                             tempSpec.name, kUseQuotes); // true -> use quotes
  351.             }
  352.         }
  353.     }
  354.     
  355.     // save the current selection as the old selection for comparison next time
  356.     //
  357.     // it's not valid on the first call, though, or if we don't have a 
  358.     // name available from standard file
  359.     
  360.     if (item != sfHookFirstCall || theUserDataRecPtr->sfrPtr->sfFile.name[0] != '\0')
  361.     {
  362.         theUserDataRecPtr->oldSelectionFSSpec = theUserDataRecPtr->sfrPtr->sfFile;
  363.     }
  364.     else
  365.     {
  366.         // on first call, empty string won't set the button correctly, 
  367.         // so invalidate oldSelection
  368.         
  369.         theUserDataRecPtr->oldSelectionFSSpec.vRefNum = 999;
  370.         theUserDataRecPtr->oldSelectionFSSpec.parID = 0;
  371.     }
  372.  
  373.     SetA4(lSaveA4);
  374.  
  375.     return item;
  376. }
  377.  
  378. static void StandardGetFolder(FileFilterYDUPP fileFilter, StandardFileReply *theSFR)
  379. {
  380.     Point                 thePt;
  381.     SFTypeList            mySFTypeList;
  382.     UserDataRec            myData;
  383.     FSSpec                tempSpec;
  384.     Boolean                folderFlag;
  385.     Boolean                wasAliasedFlag;
  386.     DlgHookYDUPP        dlgHookUPP;
  387.     ModalFilterYDUPP    myModalFilterUPP;
  388.     OSErr                err;
  389.     
  390.     // presumably we're running System 7 or later so CustomGetFile is
  391.     // available
  392.     
  393.     // set initial contents of Select button to a space
  394.     
  395.     CopyPStr("\p ", theSFR->sfFile.name);
  396.     
  397.     // point the user data parameter at the reply record so we can get to it later
  398.     
  399.     myData.sfrPtr = theSFR;
  400.  
  401.     myData.lUseA4 = GetCurrentA4();
  402.  
  403.     // display the dialog
  404.     
  405.     dlgHookUPP = NewDlgHookYDProc(SFGetFolderDialogHook);
  406.     myModalFilterUPP = NewModalFilterYDProc(SFGetFolderModalDialogFilter);
  407.     
  408.     thePt.h = thePt.v = -1;    // center dialog
  409.     
  410.     CustomGetFile(fileFilter, 
  411.                     -1,                    // show all types
  412.                     mySFTypeList,
  413.                     theSFR,
  414.                     kSFGetFolderDlgID,
  415.                     thePt,                // top left point
  416.                     dlgHookUPP,
  417.                     myModalFilterUPP,
  418.                     nil,                // activate list
  419.                     nil,                // activate proc
  420.                     &myData);
  421.                     
  422.     DisposeRoutineDescriptor(dlgHookUPP);
  423.     DisposeRoutineDescriptor(myModalFilterUPP);
  424.     
  425.     // if cancel wasn't pressed and no fatal error occurred...
  426.     
  427.     if (theSFR->sfGood)
  428.     {
  429.         // if no name is in the reply record file spec,
  430.         // use the file spec of the parent folder
  431.         
  432.         if (theSFR->sfFile.name[0] == '\0')
  433.         {
  434.             err = FSMakeFSSpec(theSFR->sfFile.vRefNum, theSFR->sfFile.parID,
  435.                                 "\p", &tempSpec);
  436.             if (err == noErr)
  437.             {
  438.                 theSFR->sfFile = tempSpec;
  439.             }
  440.             else
  441.             {
  442.                 // no name to return, forget it
  443.                 
  444.                 theSFR->sfGood = false;
  445.             }
  446.         }
  447.         
  448.         // if there is now a name in the file spec, check if it's
  449.         // for a folder or a volume
  450.         
  451.         if (theSFR->sfFile.name[0] != '\0')
  452.         {
  453.             // the parID of the root of a disk is always fsRtParID == 1
  454.             
  455.             if (theSFR->sfFile.parID == fsRtParID)
  456.             {
  457.                 theSFR->sfIsVolume = true;
  458.                 theSFR->sfIsFolder = false;    // it would be reasonable for this to be true, too
  459.             }
  460.             
  461.             // we have a valid FSSpec, now let's make sure it's not for an alias file
  462.             
  463.             err = ResolveAliasFile(&theSFR->sfFile, true, &folderFlag, &wasAliasedFlag);
  464.             if (err != noErr)
  465.             {
  466.                 theSFR->sfGood = false;
  467.             }
  468.             
  469.             // did the alias resolve to a folder?
  470.             
  471.             if (folderFlag  && ! theSFR->sfIsVolume)
  472.             {
  473.                 theSFR->sfIsFolder = true;
  474.             }
  475.         }
  476.     }
  477. }
  478.  
  479. static pascal Boolean OnlyVisibleFoldersCustomFileFilter(CInfoPBPtr myCInfoPBPtr, Ptr dataPtr)
  480. {
  481. #pragma unused (dataPtr)
  482.  
  483.     // return true if this item is invisible or a file
  484.  
  485.     Boolean visibleFlag;
  486.     Boolean folderFlag;
  487.     
  488.     visibleFlag = ! (myCInfoPBPtr->hFileInfo.ioFlFndrInfo.fdFlags & kIsInvisible);
  489.     folderFlag = (myCInfoPBPtr->hFileInfo.ioFlAttrib & 0x10);
  490.     
  491.     // because the semantics of the filter proc are "true means don't show
  492.     // it" we need to invert the result that we return
  493.     
  494.     return !(visibleFlag && folderFlag);
  495. }
  496.  
  497. static REALfolderItem SelectFolder(void)
  498. {
  499.     if (NavServicesAvailable())
  500.     {
  501.         NavReplyRecord navReply;
  502.         REALfolderItem f = nil;
  503.         FSSpec spec;
  504.  
  505.         NavChooseFolder(nil, &navReply, nil, nil, nil, nil);
  506.  
  507.         if (navReply.validRecord)
  508.         {
  509.             long itemsInList;
  510.             AEKeyword keywd;
  511.             DescType returnType;
  512.             long actualSize;
  513.  
  514.             AECountItems(&navReply.selection, &itemsInList);
  515.             if (itemsInList >= 1)
  516.             {
  517.                 AEGetNthPtr(&navReply.selection, 1, typeFSS, &keywd, &returnType, &spec, sizeof(spec), &actualSize);
  518.                 FSMakeFSSpec(spec.vRefNum, spec.parID, spec.name, &spec);
  519.                 f = REALFolderItemFromFSSpec(&spec);
  520.             }
  521.             else
  522.                 f = nil;
  523.         }
  524.  
  525.         NavDisposeReply(&navReply);
  526.         return f;
  527.     }
  528.     else
  529.     {
  530.         StandardFileReply    mySFReply;
  531.  
  532.     #ifdef powerc
  533.         StandardGetFolder(NewFileFilterYDProc(OnlyVisibleFoldersCustomFileFilter), &mySFReply);
  534.     #else
  535.         StandardGetFolder((FileFilterYDUPP) OnlyVisibleFoldersCustomFileFilter, &mySFReply);
  536.     #endif
  537.         if (mySFReply.sfGood)
  538.             return REALFolderItemFromFSSpec(&mySFReply.sfFile);
  539.         return nil;
  540.     }
  541. }
  542. #endif
  543.  
  544. REALmethodDefinition SelectFolderDefn = {
  545.     (REALproc) SelectFolder,
  546.     REALnoImplementation,
  547.     "SelectFolder as FolderItem"
  548. };
  549.  
  550. void PluginEntry(void)
  551. {
  552.     REALRegisterMethod(&SelectFolderDefn);
  553. }
  554.